import os
import re
import pykakasi

from mutagen.flac import FLAC
from mutagen.mp3 import MP3
from mutagen.dsdiff import DSDIFF
from mutagen.dsf import DSF
from mutagen.wave import WAVE
from mutagen.monkeysaudio import MonkeysAudio
from mutagen.mp4 import MP4
from mutagen import File


class Song:
    # 格式限定
    SONG_FORMAT_LIMIT = ['flac', 'mp3', 'wav', 'wave', 'dsf', 'dff', 'dsdiff', 'ape', 'm4a']

    def __init__(self, path):
        self.path: str = path
        # Tag信息
        self.title = '暂无'
        self.artist = '暂无'
        self.album = '暂无'
        self.date = '暂无'
        self.genre = '暂无'
        self.lyrics = ''
        self.cover = b''
        # 音频信息
        self.sample_rate = 0  # 采样率
        self.bits_per_sample = 0  # 位深
        self.bitrate = 0  # 比特率
        self.channels = 0  # 声道
        self.length = 0  # 持续时间
        self.audio_type = ''  # 音频类型
        self.is_hr = False  # 是否达到Hi-Res

        try:
            self.load_metadata()
        except Exception as e:
            print(e)

    def load_metadata(self):
        """读取歌曲元数据"""
        if not self.path:
            return
        # 获取文件后缀
        filetype = self.path.split('.')[-1].lower()

        if filetype == 'flac':
            audio = FLAC(self.path)
            self.audio_type = 'FLAC'
            self.parse_audio_info(audio.info)
            self.parse_flac_tag(audio)

        elif filetype == 'mp3':
            self.audio_type = 'MP3'
            audio = MP3(self.path)
            self.parse_audio_info(audio.info)
            self.parse_id3_tag(audio)

        elif filetype in ['wav', 'wave']:
            self.audio_type = 'WAV'
            audio = WAVE(self.path)
            self.parse_audio_info(audio.info)
            self.parse_id3_tag(audio)

        elif filetype == 'dsf':
            self.audio_type = 'DSF'
            audio = DSF(self.path)
            self.parse_audio_info(audio.info)
            self.parse_id3_tag(audio)

        elif filetype in ['dsdiff', 'dff']:
            self.audio_type = 'DFF'
            audio = DSDIFF(self.path)
            self.parse_audio_info(audio.info)
            self.parse_id3_tag(audio)

        elif filetype in ['ape']:
            self.audio_type = 'APE'
            audio = MonkeysAudio(self.path)
            self.parse_audio_info(audio.info)
            self.parse_ape_tag(audio)

        elif filetype in ['m4a']:
            self.audio_type = 'AAC'
            audio = MP4(self.path)
            self.parse_audio_info(audio.info)
            self.parse_mp4_tag(audio)

        else:
            self.audio_type = 'UNKNOWN'
            audio = File(self.path)
            self.parse_audio_info(audio.info)
            self.parse_id3_tag(audio)

        if self.lyrics == '' and os.path.exists(os.path.splitext(self.path)[0] + '.lrc'):
            with open(os.path.splitext(self.path)[0] + '.lrc', "r", encoding='utf-8', errors='ignore') as f:  # 打开文件
                self.lyrics = f.read()  # 读取本地歌词

    def get_lrc_dict(self):
        """根据时间戳将歌词转为字典"""
        if not self.lyrics:
            return {}
        lrc_list = self.lyrics.splitlines()
        # 时间戳正则
        func = re.compile("\\[.*?]")  # 为符合PEP8规范，反斜杠双写，实际使用一个也可以
        # 根据时间戳，转换成字典
        lrc_dict = {}
        for item in lrc_list:
            searched = func.search(item)
            if not searched:
                continue
            lrc_time = searched.group()
            time_str_list = lrc_time[1:-1].split(":")
            if not time_str_list[0].isdigit():
                continue
            lrc_time_int = int(time_str_list[0]) * 60000 + int(float(time_str_list[1]) * 1000)
            lrc_text = func.sub('', item)
            lrc_text = ' '.join(lrc_text.split())  # 去除多余空格
            if lrc_dict.get(lrc_time_int):
                lrc_dict[lrc_time_int].append(lrc_text)
            else:
                lrc_dict[lrc_time_int] = [lrc_text]
        return lrc_dict

    def parse_id3_tag(self, audio):
        """解析 ID3 Tag"""
        for item in audio:
            # 部分可能携带信息的FrameID
            if 'APIC' in item:
                self.cover = audio.get(item).data
            if 'USLT' in item:
                self.lyrics = str(audio.get(item))

        if audio.get('TIT2'):
            self.title = str(audio.get('TIT2'))
        if audio.get('TPE1'):
            self.artist = str(audio.get('TPE1'))
        if audio.get('TALB'):
            self.album = str(audio.get('TALB'))
        if audio.get('TDRC'):
            self.date = str(audio.get('TDRC'))
        if audio.get('TCON'):
            self.genre = str(audio.get('TCON'))

    def parse_flac_tag(self, audio):
        """解析 FLAC Tag"""
        if not audio.get('title') is None:
            self.title = audio.get('title')[0]
        if not audio.get('artist') is None:
            self.artist = audio.get('artist')[0]
        if not audio.get('album') is None:
            self.album = audio.get('album')[0]
        if not audio.get('date') is None:
            self.date = audio.get('date')[0]
        if not audio.get('genre') is None:
            self.genre = audio.get('genre')[0]
        if not audio.get('lyrics') is None:
            self.lyrics = audio.get('lyrics')[0]
        if audio.pictures:
            self.cover = audio.pictures[0].data

    def parse_mp4_tag(self, audio):
        """解析 MP4 Tag"""
        if not audio.get('©nam') is None:
            self.title = audio.get('©nam')[0]
        if not audio.get('©ART') is None:
            self.artist = audio.get('©ART')[0]
        if not audio.get('©alb') is None:
            self.album = audio.get('©alb')[0]
        if not audio.get('©day') is None:
            self.date = audio.get('©day')[0]
        if not audio.get('©gen') is None:
            self.genre = audio.get('©gen')[0]
        if not audio.get('©lyr') is None:
            self.lyrics = audio.get('©lyr')[0]
        if not audio.get('covr') is None:
            self.cover = audio.get('covr')[0]

    def parse_ape_tag(self, audio):
        """解析 APEv2 Tag"""
        if audio.get('COVER ART (FRONT)'):
            b_value = audio.get('COVER ART (FRONT)')
            if b'\0' in b_value.value:
                self.cover = b_value.value.split(b'\0', 1)[-1]
        if audio.get('TITLE'):
            self.title = str(audio.get('TITLE'))
        if audio.get('ARTIST'):
            self.artist = str(audio.get('ARTIST'))
        if audio.get('ALBUM'):
            self.album = str(audio.get('ALBUM'))
        if audio.get('YEAR'):
            self.date = str(audio.get('YEAR'))
        if audio.get('GENRE'):
            self.genre = str(audio.get('GENRE'))
        for item in audio:
            if 'LYRICS' in item:
                self.lyrics = str(audio.get(item))

    def parse_audio_info(self, audio_info):
        """解析音频信息"""
        self.sample_rate = audio_info.sample_rate
        self.channels = audio_info.channels
        self.length = audio_info.length

        if self.audio_type != 'APE':
            self.bitrate = audio_info.bitrate

        if self.audio_type != 'MP3':
            self.bits_per_sample = audio_info.bits_per_sample
            if self.audio_type in ['DSF', 'DFF'] and self.bits_per_sample == 1:
                self.is_hr = True
            elif self.bits_per_sample > 16 and self.sample_rate > 44100:
                self.is_hr = True

    def __str__(self):
        """重写打印信息"""
        if self.audio_type == 'MP3':
            return f'采样率：{self.sample_rate}, 比特率：{self.bitrate}, ' \
                   f'声道：{self.channels}, 持续时间：{self.length}，文件类型：{self.audio_type}，Hi-Res：{self.is_hr}'
        else:
            return f'采样率：{self.sample_rate}, 位深：{self.bits_per_sample}, 比特率：{self.bitrate}, ' \
                   f'声道：{self.channels}, 持续时间：{self.length}，文件类型：{self.audio_type}，Hi-Res：{self.is_hr}'

    @staticmethod
    def get_romaji(text):
        """获取罗马音"""
        def get_all_str(t):
            kks = pykakasi.kakasi()
            t = ' '.join(t)  # 插入空格
            result = kks.convert(t)
            _text = ''
            for _item in result:
                _text += _item['hepburn']
            return f' {_text} '

        # 匹配日文字符的正则表达式模式，包括平假名、片假名和CJK统一汉字，以及特殊的日文汉字字符"々"和"〇"
        pattern = re.compile(r'([\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u3005\u3007]+)')
        # 将日文字符转换为罗马音，其他部分保持原样
        parts = re.split(pattern, text)
        processed_parts = [get_all_str(part) if re.match(pattern, part) else part for part in parts]
        processed_text = ''.join(processed_parts)
        processed_text = ' '.join(processed_text.split())  # 去除多余空格
        return processed_text

    @staticmethod
    def contains_japanese(text):
        """判断日文字符"""
        # Unicode范围：平假名[\u3040-\u309F]、片假名[\u30A0-\u30FF]、CJK统一汉字[\u4E00-\u9FFF]、々[\u3005]、〇[\u3007]
        pattern = re.compile(r'[\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u3005\u3007]')
        return bool(re.search(pattern, text))
